"""控制模拟器的类"""
import json
import os
import random
import time
import cv2
import numpy as np

from utils.lg import logger


class Console:
    """
    模拟器控制类
    """

    def __init__(self, settings: dict[str, str], auto_connect: bool = True):
        """
        :param settings: 配置信息字典
        :param auto_connect: 是否自动连接模拟器，默认为True
            root_path: 模拟器根目录
            console_path: 模拟器exe程序路径
            adb_path: 模拟器adb程序路径
        """
        self.settings = settings
        self.root_path = self.settings['root_path']
        if not os.path.exists(self.root_path):
            logger.warning(f"不存在的路径：{self.root_path}")
            raise FileNotFoundError(f"不存在的路径：{self.root_path}")
        # 配置Mumu模拟器的启动路径和adb程序的启动路径
        self.console_path = os.path.join(self.root_path, self.settings['exe_path'])
        if not os.path.exists(self.console_path):
            logger.warning(f"不存在的路径：{self.console_path}")
            raise FileNotFoundError(f"不存在的路径：{self.console_path}")
        self.adb_path = os.path.join(self.root_path, self.settings['adb_path'])
        if not os.path.exists(self.adb_path):
            logger.warning(f"不存在的路径：{self.adb_path}")
            raise FileNotFoundError(f"不存在的路径：{self.adb_path}")
        if auto_connect:
            self.connect(self.settings['ip'], self.settings['port'])
        # 获取屏幕长宽
        result = self.adb_execute("shell wm size")
        self.height, self.width = result.decode().strip().split(' ')[-1].split('x')

    @logger.catch
    def cmd_execute(self, cmd_command: str, logs: str = ""):
        """
        在模拟器根目录执行命令行语句
        :param cmd_command: 命令行语句
        :param logs: 日志输出，默认为命令行输出
        :return: 命令行输出(二进制)
        """
        cmd = " ".join([f'"{self.console_path}"', cmd_command])
        process = os.popen(cmd)
        result = process.buffer.read()
        process.close()
        if not logs:
            logger.debug(result.decode().strip())
        else:
            logger.debug(logs)
        return result

    @logger.catch
    def adb_execute(self, adb_command: str, logs=""):
        """
        使用 adb.exe 执行adb命令
        :param adb_command: adb命令
        :param logs: 日志输出，默认为命令行输出
        :return: 命令行输出(二进制)
        """
        adb = " ".join([f'"{self.adb_path}"', adb_command])
        process = os.popen(adb)
        result = process.buffer.read()
        process.close()
        if not logs:
            logger.debug(result.decode().strip())
        else:
            logger.debug(logs)
        return result

    def connect(self, ip: str, port: str):
        """
        连接模拟器
        :param ip: 模拟器ip
        :param port: 模拟器端口
        """
        cmd = f"connect {ip}:{port}"
        self.adb_execute(cmd)

    def disconnect(self, ip: str, port: str):
        """
        断开模拟器连接
        :param ip: 模拟器ip
        :param port: 模拟器端口
        """
        cmd = f"disconnect {ip}:{port}"
        self.adb_execute(cmd)

    def disconnect_all(self):
        """
        断开所有模拟器连接
        """
        cmd = f"disconnect"
        self.adb_execute(cmd)

    def make_screenshot(self, saved: bool = False, saved_path: str = ""):
        """
        给当前模拟器截屏
        :param saved: 是否保存
        :param saved_path: 照片保存路径
        """
        cmd = f"shell screencap -p"
        result = self.adb_execute(cmd, f"截屏")
        # 图片的换行符号需要换一下
        img = result.replace(b"\r\n", b"\n")
        if saved and saved_path != "":
            with open(saved_path, "wb") as f:
                f.write(img)
                logger.debug(f"截屏保存于: {saved_path}")
        img = cv2.imdecode(np.array(bytearray(img), dtype=np.uint8), cv2.IMREAD_COLOR)
        return img

    def tap(self, x: int, y: int, random_range: int = 4, times: int = 1, internal: float = 0.05, delay: float = 0.05):
        """
        点击按钮
        :param x:
        :param y:
        :param random_range: 偏移误差
        :param times: 点击次数
        :param internal: 多次点击时每次点击的间隔
        :param delay: 每次点击的延迟
        """
        rr = int(random_range / 2)
        for i in range(times):
            xx = x + random.randint(-rr, rr)
            yy = y + random.randint(-rr, rr)
            cmd = f"shell input tap {xx} {yy}"
            self.adb_execute(cmd, f"点击坐标: ({xx}, {yy})")
            time.sleep(internal)
        time.sleep(delay)

    def swipe(self, x0: int, y0: int, x1: int, y1: int, ms: int = 200, random_range: int = 4, delay: float = 0.05):
        """
        滑动
        :param x0: 起始坐标 x
        :param y0: 起始坐标 y
        :param x1: 终点坐标 x
        :param y1: 终点坐标 y
        :param ms: 滑动时间
        :param delay: 滑动后的等待延迟时间
        :param random_range: 偏移误差
        """
        rr = random_range // 2
        xx0 = x0 + random.randint(-rr, rr)
        yy0 = y0 + random.randint(-rr, rr)
        xx1 = x1 + random.randint(-rr, rr)
        yy1 = y1 + random.randint(-rr, rr)
        mms = ms + random.randint(-rr, rr) * 10
        cmd = f"shell input swipe {xx0} {yy0} {xx1} {yy1} {mms}"
        self.adb_execute(cmd, f"滑动: ({xx0}, {yy0}) -> ({xx1} {yy1}) 用时: {mms}ms")
        time.sleep(delay)

    def __del__(self):
        # 断开所有模拟器连接
        self.disconnect_all()


class MumuConsole(Console):
    def __init__(self, auto_connect=True):
        with open(r"data\settings.json", 'r', encoding='utf-8') as f:
            mumu_settings = json.load(f)['MumuConsole']
        super().__init__(
            mumu_settings,
            auto_connect
        )
